package com.yh.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;

/**
 * 结果集处理工具（可以对多个任意实体类性的结果集进行交并集的处理）
 *
 * @author yuhuan
 * @date 2022/01/13
 */
public class MergeSearchResult<T> {
    private List<T> unionResult;
    private List<T> interResult;

    /**
     * 并集处理（合并多个相同类型元素的结果集并使用默认规则剔除重复的数据）
     * 提示：结果集中的元素必须要重写toString方法，否则处理的结果可能不准确
     */
    public List<T> union(Collection<T>... searchResult) {
        this.unionResult = mergeResult((p1, p2) -> p1.equals(p2), searchResult);
        return this.unionResult;
    }

    /**
     * 交集处理（在多个相同类型元素的结果集中取出共有的元素）
     * 提示：结果集中的元素必须要重写toString方法，否则处理的结果可能不准确
     */
    public List<T> inter(Collection<T>... searchResult) {
        interResult = new ArrayList<>();
        // 记录总共有多少个结果集
        int searchResultLength = 0;
        // 合并后的结果集
        Collection<T> collection = null;
        // 将所有结果集中的数据合并起来（注意：合并时不能剔除重复元素）
        for (Collection<T> singleCollection : searchResult) {
            if (collection == null) {
                collection = singleCollection;
            } else {
                collection.addAll(singleCollection);
            }
            // 每合并一次，总记录数就加1
            searchResultLength++;
        }
        // 如果合并后的结果还是为空就直接结束
        if (collection == null) {
            return interResult;
        }
        // 将合并起来的结果集按照toString进行分组
        Map<String, List<T>> collect = collection.stream().collect(Collectors.groupingBy(e -> e.toString()));
        // 遍历分组后得到的所有元素集，如果元素集的长度等于开始时记录的结果集的总长度，就取出第一位元素并添加到交集结果中
        for (List<T> value : collect.values()) {
            if (value.size() == searchResultLength) {
                interResult.add(value.get(0));
            }
        }
        return interResult;
    }

    /**
     * 合并多个结果集并根据指定规则剔除重复元素
     *
     * @Param predicate 在检查筛选的结果集时，需要传递一个函数作为重要的排重依据
     * 如：(p1, p2)->p1.getProductId()==p2.getProductId()
     * @Param searchResult 任何数量的需要排重的结果集
     */
    public List<T> mergeResult(BiPredicate<T, T> predicate, Collection<T>... searchResult) {
        // 储存最终的筛选结果
        List<T> mergeResult = new ArrayList<T>();
        // 遍历可变参数集合
        for (Collection<T> singleCollection : searchResult) {
            // 遍历单个需要排重的结果集，如果在筛选结果集中不存在，则将其添加到筛选结果中
            for (T element : singleCollection) {
                if (isRepeat(predicate, mergeResult, element) == false) {
                    mergeResult.add(element);
                }
            }
        }
        return mergeResult;
    }

    /**
     * 检查某个元素在筛选出来的结果集中是否已经存在
     *
     * @Param predicate 用来作为排重依据的函数
     * 如：(p1, p2)->p1.getProductId()==p2.getProductId()
     * @Param mergeResult 需要检查的结果集
     * @Param param 需要检查的元素
     */
    private boolean isRepeat(BiPredicate<T, T> predicate, Collection<T> mergeResult, T param) {
        boolean isRepeat = false;
        // 根据排重依据循环筛选结果集
        for (T element : mergeResult) {
            isRepeat = predicate.test(element, param);
            if (isRepeat) {
                break;
            }
        }
        return isRepeat;
    }
}